package edu.pku.sei.transformation.editor.views.tree;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.gef.dnd.TemplateTransfer;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;

import edu.pku.sei.modeler.edit.parts.AbstractNodeEditPart;
import edu.pku.sei.modeler.explorer.MetaModelView;
import edu.pku.sei.modeler.model.core.MetaModelElement;
import edu.pku.sei.modeler.model.core.MetaPrimitiveType;
import edu.pku.sei.modeler.model.structure.MetaNode;
import edu.pku.sei.transformation.editor.actions.ChoosePinToSelection;
import edu.pku.sei.transformation.editor.actions.ChooseTreeViewInputAction;
import edu.pku.sei.transformation.editor.actions.MetaModelTreeActionGroup;
import edu.pku.sei.transformation.editor.model.ModelType;
import edu.pku.sei.transformation.editor.model.Transformation;
import edu.pku.sei.transformation.editor.model.TypedModel;

public class TypedModelView extends ViewPart {
	public static final String SRC_META_MODEL_ID = "edu.pku.sei.modeler.transformationeditor.metaModelView";

	public static final String DST_META_MODEL_ID = "edu.pku.sei.modeler.qvteditor.tgtMetaModelView";

	// private OpenNodeAction doubleClickAction;

	private TreeViewer tv;
	MetaModelTreeActionGroup actionGroup;
	private boolean pinToSelection = true;

	public boolean isPinToSelection() {
		return pinToSelection;
	}

	public void setPinToSelection(boolean pinToSelection) {
		this.pinToSelection = pinToSelection;
	}

	@Override
	public void createPartControl(Composite parent) {
		parent.setLayout(new FillLayout());
		tv = new TreeViewer(parent, SWT.BORDER | SWT.MULTI);
		tv.setContentProvider(new ModelTreeViewerContentProvider());
		tv.setLabelProvider(new ModelTreeViewerLabelProvider());
		actionGroup = new MetaModelTreeActionGroup(tv);
		actionGroup.fillContextMenu(new MenuManager());
		Object inputObj = new ArrayList();
		tv.setInput(inputObj);

		HookOpenNodeAction();
		HookDragAction();
		//updateViewPullDownMenu();
		this.getSite().setSelectionProvider(tv);

		tv.addSelectionChangedListener(new ISelectionChangedListener() {
			public void selectionChanged(SelectionChangedEvent event) {
				actionGroup.fillContextMenu(new MenuManager());
			}
		});
		
		getViewSite().getActionBars().getToolBarManager().add(new ChoosePinToSelection(this));
		
		getSite().getPage().addSelectionListener(new ISelectionListener() {
			
			@Override
			public void selectionChanged(IWorkbenchPart part, ISelection selection) {
				if (part instanceof TypedModelView)
					return;
				if (selection instanceof IStructuredSelection
						&& ((IStructuredSelection) selection).toList()
								.size() > 0) {
					Object firstSelectedElement = ((IStructuredSelection) selection)
					.toList().get(0);
					//if pin to selection, change input with selection
					if (pinToSelection) {
						updateInput(firstSelectedElement);
						updateViewPullDownMenu(firstSelectedElement);
					}
				}
			}
		});
	}
	
	private boolean getMetaModels(Object element, List metaModels) {
		MetaModelElement modelElement;
		
		if (element instanceof MetaModelElement)
			modelElement = (MetaModelElement) element;
		else if (element instanceof AbstractNodeEditPart)
			modelElement = ((MetaNode)((AbstractNodeEditPart)element).getModel()).getModelElement();
		else
			return false;
		
		if (modelElement instanceof ModelType) {
			metaModels.add(modelElement);
			return true;
		} else if (modelElement instanceof Transformation) {
			Transformation trans = (Transformation) modelElement;
			for (Object tfp : trans.getTypedModel()) {
				Object type = ((TypedModel)tfp).getType();
				if (type instanceof ModelType && ((ModelType)type).getType()!=null) {
					metaModels.add(((ModelType)type).getType());					
				} else if (type instanceof MetaPrimitiveType) {
					metaModels.add(type);
				}
			}
			return true;
		} 	
		
		return false;
	}
	
	private void updateInput(Object firstSelectedElement) {
		ArrayList input = new ArrayList();
		if (getMetaModels(firstSelectedElement, input))
			tv.setInput(input);
	}
	
	private void updateViewPullDownMenu(Object firstSelectedElement) {
		ArrayList input = new ArrayList();
		if (getMetaModels(firstSelectedElement, input)) {
			IMenuManager menu = getViewSite().getActionBars().getMenuManager();
			menu.removeAll();
			for (Object obj : input) {
					String label = obj.toString();
					ChooseTreeViewInputAction a = new ChooseTreeViewInputAction(
							getTv(), obj, label);
					menu.add(a);
			}
			getViewSite().getActionBars().updateActionBars();
		}
	}
	/**
	 * @deprecated
	 */
	private void updateViewPullDownMenu() {
		IMenuManager menu = getViewSite().getActionBars().getMenuManager();
		menu.removeAll();
		Transformation t = this.getSelectedTransformation();
		if (t != null) {
			for (Object obj : t.getModelParameter()) {
				if (obj instanceof TypedModel) {
					String label = ((TypedModel) obj).getName();
					ChooseTreeViewInputAction a = new ChooseTreeViewInputAction(
							getTv(), ((TypedModel) obj).getType(), label);
					menu.add(a);
				}
			}
		}
		getViewSite().getActionBars().updateActionBars();
		
	}

	private void HookDragAction() {
		TextTransfer textTransfer = TextTransfer.getInstance();
		TemplateTransfer templateTransfer = TemplateTransfer.getInstance();
		DragSource source = new DragSource(tv.getTree(), DND.DROP_MOVE
				| DND.DROP_COPY);
		source.setTransfer(new Transfer[] { templateTransfer });
		// source.addDragListener(new MetaModelTreeNodeDragSourceListener(tv));

	}

	private void HookOpenNodeAction() {
		// doubleClickAction = new OpenNodeAction();
		// doubleClickAction.setTv(this.tv);
		// doubleClickAction.setWorkbenchPage(getViewSite().getPage());
		// this.tv.addDoubleClickListener(new IDoubleClickListener() {
		// public void doubleClick(DoubleClickEvent event) {
		// doubleClickAction.run();
		// }
		// });
	}

	@Override
	public void setFocus() {
		//updateViewPullDownMenu();
		tv.getControl().setFocus();
		actionGroup.fillContextMenu(new MenuManager());

	}

	public TreeViewer getTv() {
		return tv;
	}

	public void setTv(TreeViewer tv) {
		this.tv = tv;
	}
	/**
	 * @deprecated
	 * @return
	 */
	public Transformation getSelectedTransformation() {
		IWorkbenchPage workbenchpage = PlatformUI.getWorkbench()
				.getActiveWorkbenchWindow().getActivePage();
		if (workbenchpage != null) {
			for (int i = 0; i < workbenchpage.getViewReferences().length; i++) {
				IViewPart viewPart = workbenchpage.getViewReferences()[i]
						.getView(false);
				if (viewPart instanceof MetaModelView) {
					ISelection selection = viewPart.getSite()
							.getSelectionProvider().getSelection();
					if (selection instanceof IStructuredSelection
							&& ((IStructuredSelection) selection).toList()
									.size() > 0) {
						Object obj = ((IStructuredSelection) selection)
								.toList().get(0);
						if (obj instanceof Transformation) {
							return (Transformation) obj;
						}
					}
				}
			}
		}
		return null;
	}
}
